package com.enba.rbacadmin.module.auth.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.enba.boot.core.context.SecurityContextHolder;
import com.enba.boot.core.exception.BizException;
import com.enba.boot.jwt.JwtHelper;
import com.enba.boot.redis.RedisOperator;
import com.enba.boot.sign.utils.MD5Util;
import com.enba.rbacadmin.constant.CommonConstant;
import com.enba.rbacadmin.constant.UserConstant;
import com.enba.rbacadmin.enums.CaptchaSceneEnum;
import com.enba.rbacadmin.enums.OrgTypeEnum;
import com.enba.rbacadmin.manager.UserMenuManager;
import com.enba.rbacadmin.module.auth.bo.UserRoleBo;
import com.enba.rbacadmin.module.auth.controller.ex.UserExCode;
import com.enba.rbacadmin.module.auth.controller.req.LoginReq;
import com.enba.rbacadmin.module.auth.controller.req.RegisterReq;
import com.enba.rbacadmin.module.auth.controller.req.UpdatePasswordReq;
import com.enba.rbacadmin.module.auth.controller.req.UserAddUpdateReq;
import com.enba.rbacadmin.module.auth.controller.req.UserBindRoleReq;
import com.enba.rbacadmin.module.auth.controller.req.UserPageReq;
import com.enba.rbacadmin.module.auth.controller.resp.UserPageResp;
import com.enba.rbacadmin.module.auth.entity.Org;
import com.enba.rbacadmin.module.auth.entity.User;
import com.enba.rbacadmin.module.auth.entity.UserRole;
import com.enba.rbacadmin.module.auth.mapper.OrgMapper;
import com.enba.rbacadmin.module.auth.mapper.UserMapper;
import com.enba.rbacadmin.module.auth.mapper.UserRoleMapper;
import com.enba.rbacadmin.module.auth.service.IUserService;
import com.enba.rbacadmin.utils.CodeGenerator;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 用户表 服务实现类
 *
 * @author 恩爸编程
 * @since 2025-02-28
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

  @Autowired private JwtHelper jwtHelper;
  @Autowired private RedisOperator redisOperator;
  @Autowired private UserMenuManager usrMenuManager;
  @Autowired private CodeGenerator codeGenerator;
  @Autowired private OrgMapper orgMapper;
  @Autowired private UserRoleMapper userRoleMapper;

  @Value("${captcha.open: true}")
  private Boolean captchaOpen;

  @Override
  public Boolean register(RegisterReq req) {
    // 必填校验
    if (req.getUsername() == null || req.getPassword() == null) {
      BizException.throwEx("用户名，密码为空");
    }

    // 验证用户是否已存在
    if (this.getOne(Wrappers.<User>lambdaQuery().eq(User::getUsername, req.getUsername()))
        != null) {
      BizException.throwEx(UserExCode.USER_EXIST);
    }

    // 落库
    User user = new User();
    user.setUsername(req.getUsername());
    user.setPassword(MD5Util.encrypt(req.getPassword()));
    boolean save = this.save(user);

    if (save) {
      // TODO  注册成功发送邮件，积分等

    }

    return save;
  }

  @Override
  public String login(LoginReq req) {
    // 入参校验
    req.check();

    // 校验验证码
    if (captchaOpen) {
      // 登录开启验证码校验
      String captchaValue = req.getCaptchaValue();
      Object redisValue =
          redisOperator.get(CaptchaSceneEnum.LOGIN_CAPTCHA.getRedisFullKey(req.getCaptchaKey()));
      if (!captchaValue.equals(redisValue)) {
        BizException.throwEx("验证码错误");
      }
    }

    // 验证用户是否已存在
    User one = this.getOne(Wrappers.<User>lambdaQuery().eq(User::getUsername, req.getUsername()));
    if (Objects.isNull(one)) {
      BizException.throwEx(UserExCode.USER_NOT_EXIST);
    }

    // 用户禁用状态校验
    if (one.getStatus() == CommonConstant.no) {
      BizException.throwEx(UserExCode.USER_BE_DISABLED);
    }

    // 密码正确，登陆成功
    if (MD5Util.encrypt(req.getPassword()).equals(one.getPassword())) {

      // 获取用户角色标识
      Set<String> rolesSet = usrMenuManager.getUserRoleKey(one.getId());

      // 获取用户权限码值
      Set<String> permsSet = usrMenuManager.getUserPermsKey(one.getId());

      // 缓存角色
      redisOperator.set(UserConstant.login_user_role_key + one.getId(), rolesSet, 60 * 60 * 24);
      // 缓存权限
      redisOperator.set(UserConstant.login_user_perms_key + one.getId(), permsSet, 60 * 60 * 24);

      //  登录成功，生成token，返回token
      Map<String, Object> claims = Maps.newHashMap();
      claims.put("userId", one.getId());
      claims.put("username", one.getUsername());
      claims.put("role", rolesSet);
      claims.put("perms", permsSet);

      // 删除验证码缓存
      redisOperator.delete(CaptchaSceneEnum.LOGIN_CAPTCHA.getRedisFullKey(req.getCaptchaKey()));
      return jwtHelper.createToken(claims);
    } else {
      // 密码错误
      BizException.throwEx(UserExCode.USER_PSD_ERROR);
      return null;
    }
  }

  @Override
  public Boolean logout() {
    Long userId = SecurityContextHolder.getUserId();

    // 删除权限缓存
    redisOperator.delete(UserConstant.login_user_perms_key + userId);

    // 删除角色缓存
    redisOperator.delete(UserConstant.login_user_role_key + userId);

    return true;
  }

  @Override
  public Boolean saveOrUpdate(UserAddUpdateReq req) {
    Long id = req.getId();

    if (id == null) {
      User one =
          this.getOne(
              Wrappers.<User>lambdaQuery()
                  .eq(User::getUsername, req.getUsername())
                  .ne(User::getDeleted, CommonConstant.no));
      if (one != null) {
        // 用户名已存在
        BizException.throwEx(UserExCode.USER_EXIST);
      }

      // 检查部门ID是否存在
      Org org =
          orgMapper.selectOne(
              Wrappers.<Org>lambdaQuery()
                  .eq(Org::getId, req.getDeptId())
                  .eq(Org::getDeleted, CommonConstant.no));
      if (org == null) {
        // 部门不存在
        BizException.throwEx(UserExCode.DEPT_NOT_EXIST);
      }
      if (!OrgTypeEnum.BM.getCode().equals(org.getOrgType())) {
        // 部门类型错误
        BizException.throwEx(UserExCode.DEPT_TYPE_ERROR);
      }
      // 检查部门状态
      if (org.getStatus() == CommonConstant.no) {
        // 部门禁用
        BizException.throwEx(UserExCode.DEPT_BE_DISABLED);
      }

      // 新增
      User user = new User();
      user.setUserCode(codeGenerator.userGenerateCode());
      user.setUsername(req.getUsername());
      // 新增用户时初始化密码为123456
      user.setPassword(MD5Util.encrypt("123456"));
      user.setMobile(req.getMobile());
      user.setEmail(req.getEmail());
      user.setDeptId(req.getDeptId());
      user.setOrgId(org.getParentId());
      user.setStatus(req.getStatus());
      return this.save(user);
    } else {
      // 更新(只支持更新用户名，手机号，邮箱，状态)
      User byId = this.getById(id);
      if (byId == null) {
        BizException.throwEx(UserExCode.USER_NOT_EXIST);
      }

      // 根据用户名查找
      User one =
          this.getOne(
              Wrappers.<User>lambdaQuery()
                  .eq(User::getUsername, req.getUsername())
                  .eq(User::getDeleted, CommonConstant.no));
      if (one != null && !one.getId().equals(byId.getId())) {
        // 用户名已存在
        BizException.throwEx(UserExCode.USER_EXIST);
      }

      return this.update(
          Wrappers.<User>lambdaUpdate()
              .set(Objects.nonNull(req.getUsername()), User::getUsername, req.getUsername())
              .set(Objects.nonNull(req.getMobile()), User::getMobile, req.getMobile())
              .set(Objects.nonNull(req.getEmail()), User::getEmail, req.getEmail())
              .set(Objects.nonNull(req.getStatus()), User::getStatus, req.getStatus())
              .eq(User::getId, id));
    }
  }

  @Override
  public Page<UserPageResp> userFindPage(UserPageReq req) {
    // 搜索条件
    LambdaQueryWrapper<User> queryWrapper =
        Wrappers.<User>lambdaQuery().orderByDesc(User::getCreateTime);
    // 用户编码
    queryWrapper.eq(
        StringUtils.isNotBlank(req.getUserCode()), User::getUserCode, req.getUserCode());
    // 用户名称
    queryWrapper.eq(
        StringUtils.isNotBlank(req.getUsername()), User::getUsername, req.getUsername());
    // 创建时间开始
    queryWrapper.ge(
        Objects.nonNull(req.getCreateTimeStart()), User::getCreateTime, req.getCreateTimeStart());
    // 创建时间结束
    queryWrapper.le(
        Objects.nonNull(req.getCreateTimeEnd()), User::getCreateTime, req.getCreateTimeEnd());

    // 执行搜索
    Page<User> userPage = this.page(new Page<>(req.getPageNum(), req.getPageSize()), queryWrapper);

    List<UserPageResp> userPageList =
        BeanUtil.copyToList(userPage.getRecords(), UserPageResp.class);

    // 组织信息坐成
    Set<Long> deptIdSet =
        userPageList.stream().map(UserPageResp::getDeptId).collect(Collectors.toSet());

    // TODO 组织信息不经常变动，可以缓存到Redis中，减少数据库查询次数
    // 部门信息
    Map<Long, Org> deptMap =
        orgMapper
            .selectList(
                Wrappers.<Org>lambdaQuery()
                    .in(Org::getId, deptIdSet)
                    .eq(Org::getDeleted, CommonConstant.no))
            .stream()
            .collect(Collectors.toMap(Org::getId, Function.identity()));

    // 获取相关组织数据（集团，公司，部门）
    Set<Long> orgIdSet =
        deptMap.values().stream()
            .map(Org::getOrgPath)
            .map(e -> e.split(","))
            .flatMap(Arrays::stream)
            .map(Long::valueOf)
            .collect(Collectors.toSet());

    // 获取组织信息
    Map<Long, Org> orgMap =
        orgMapper
            .selectList(
                Wrappers.<Org>lambdaQuery()
                    .in(Org::getId, orgIdSet)
                    .eq(Org::getDeleted, CommonConstant.no))
            .stream()
            .collect(Collectors.toMap(Org::getId, Function.identity()));

    // 角色信息组装
    Set<Long> userIdSet =
        userPageList.stream().map(UserPageResp::getId).collect(Collectors.toSet());
    Set<UserRoleBo> roleNameSet = this.getBaseMapper().getRoleName(userIdSet);

    // 用户 -》 角色
    Map<Long, List<UserRoleBo>> collectMap =
        roleNameSet.stream().collect(Collectors.groupingBy(UserRoleBo::getUserId));

    // 赋值
    userPageList.forEach(
        e -> {
          // 部门
          e.setDeptName(orgMap.getOrDefault(e.getDeptId(), new Org()).getOrgName());
          // 公司
          e.setOrgName(orgMap.getOrDefault(e.getOrgId(), new Org()).getOrgName());
          // 角色
          e.setRoleName(
              collectMap.getOrDefault(e.getId(), new ArrayList<>()).stream()
                  .map(UserRoleBo::getRoleName)
                  .collect(Collectors.joining(",")));
        });

    Page<UserPageResp> retPage = new Page<>();
    retPage.setTotal(userPage.getTotal());
    retPage.setRecords(userPageList);

    return retPage;
  }

  @Override
  public boolean bindRole(UserBindRoleReq req) {
    // 先删除用户关联角色
    userRoleMapper.delete(
        Wrappers.<UserRole>lambdaQuery().eq(UserRole::getUserId, req.getUserId()));

    // 重新绑定用户角色
    req.getRoleIdSet()
        .forEach(
            roleId -> {
              UserRole userRole = new UserRole();
              userRole.setUserId(req.getUserId());
              userRole.setRoleId(roleId);
              userRoleMapper.insert(userRole);
            });

    return true;
  }

  @Override
  public boolean updatePassword(UpdatePasswordReq req) {
    return this.getBaseMapper()
            .update(
                Wrappers.<User>lambdaUpdate()
                    .eq(User::getId, req.getUserId())
                    .set(User::getPassword, MD5Util.encrypt(req.getNewPassword())))
        > 0;
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public boolean delete(Integer id) {
    // 删除用户
    this.getBaseMapper().deleteById(id);

    // 删除用户角色关联
    return userRoleMapper.delete(Wrappers.<UserRole>lambdaQuery().eq(UserRole::getUserId, id)) > 0;
  }

  @Override
  public boolean deleteBatch(List<Integer> ids) {
    // 删除用户
    this.getBaseMapper().deleteByIds(ids);

    // 删除用户角色关联
    return userRoleMapper.delete(Wrappers.<UserRole>lambdaQuery().in(UserRole::getUserId, ids)) > 0;
  }
}
